package com.gx.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gx.common.config.flowable.FlowServiceFactory;
import com.gx.model.dto.common.BaseQueryDto;
import com.gx.model.pojo.TbBusinessDeploy;
import com.gx.model.pojo.TbBusinessTask;
import com.gx.model.vo.FlowLineVo;
import com.gx.model.vo.FlowNodeVo;
import com.gx.model.vo.FlowProcDefVo;
import com.gx.service.FlowDefinitionService;
import com.gx.service.FlowInfoService;
import com.gx.service.TbBusinessDeployService;
import com.gx.service.TbBusinessTaskService;
import com.gx.utils.TenantThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static com.gx.common.constant.ProcessConstants.BPMN_FILE_SUFFIX;


/**
 * @Author Liu-pj
 * @Date 2022/8/24 15:26
 * @Desc 流程定义操作
 **/
@Service
@Slf4j
public class FlowDefinitionServiceImpl extends FlowServiceFactory implements FlowDefinitionService {
    @Autowired
    TbBusinessDeployService deployBusinessService;

    @Autowired
    TbBusinessTaskService taskBusinessService;

    @Autowired
    FlowInfoService flowInfoService;


    /**
     * 获取节点列表
     *
     * @param processId 流程定义id
     * @return 节点列表
     */
    @Override
    public List<FlowNodeVo> getNodeList(String processId) {
        List<Process> processList = repositoryService.getBpmnModel(processId).getProcesses();
        System.out.println("processes size:" + processList.size());
        ArrayList<FlowNodeVo> nodeInfoList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(processList)) {
            for (Process process : processList) {
                Collection<FlowElement> flowElements = process.getFlowElements();
                if (CollectionUtil.isNotEmpty(flowElements)) {
                    for (FlowElement flowElement : flowElements) {
                        if (flowElement instanceof UserTask) {
                            String id = flowElement.getId();
                            String name = flowElement.getName();
                            String assignee = ((UserTask) flowElement).getAssignee();
                            String skipExpression = ((UserTask) flowElement).getSkipExpression();
                            List<String> candidateGroups = ((UserTask) flowElement).getCandidateGroups();
                            List<String> candidateUsers = ((UserTask) flowElement).getCandidateUsers();
                            FlowNodeVo flowNodeVo = new FlowNodeVo();
                            flowNodeVo.setNodeId(id);
                            flowNodeVo.setNodeName(name);
                            flowNodeVo.setAss(assignee);
                            flowNodeVo.setSkip(skipExpression);
                            flowNodeVo.setCandidateGroups(candidateGroups);
                            flowNodeVo.setCandidateUsers(candidateUsers);
                            nodeInfoList.add(flowNodeVo);
                        }
                    }
                    System.out.println("节点信息: " + JSON.toJSONString(nodeInfoList));
                }
            }
        }
        return nodeInfoList;
    }

    /**
     * 获取走线列表
     *
     * @param processId 流程定义id
     * @return 节点列表
     */
    @Override
    public List<FlowLineVo> getLineList(String processId) {
        List<Process> processList = repositoryService.getBpmnModel(processId).getProcesses();
        System.out.println("processes size:" + processList.size());
        ArrayList<FlowLineVo> lineInfoList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(processList)) {
            for (Process process : processList) {
                Collection<FlowElement> flowElements = process.getFlowElements();
                if (CollectionUtil.isNotEmpty(flowElements)) {
                    for (FlowElement flowElement : flowElements) {
                        if (flowElement instanceof SequenceFlow) {
                            FlowLineVo flowLineVo = new FlowLineVo();
                            flowLineVo.setId(flowElement.getId());
                            flowLineVo.setName(flowElement.getName());
                            flowLineVo.setConditionExpression(((SequenceFlow) flowElement).getConditionExpression());
                            FlowElement targetFlowElement = ((SequenceFlow) flowElement).getTargetFlowElement();
                            if (targetFlowElement != null) {
                                String id = targetFlowElement.getId();
                                flowLineVo.setNextTaskId(id);
                                flowLineVo.setNextTaskName(targetFlowElement.getName());
                            }
                            lineInfoList.add(flowLineVo);
                        }
                    }
                    System.out.println("节点信息: " + JSON.toJSONString(lineInfoList));
                }
            }
        }
        return lineInfoList;
    }

    /**
     * 流程定义列表
     *
     * @return 流程定义分页列表数据
     */
    @Override
    public Page<FlowProcDefVo> list(BaseQueryDto queryDto) {
        Integer currPage = queryDto.getCurrPage();
        Integer pageSize = queryDto.getPageSize();
        Page<FlowProcDefVo> page = new Page<>();
        // 流程定义列表数据查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionTenantId(TenantThreadLocal.get())
                .orderByProcessDefinitionId()
                .orderByProcessDefinitionVersion()
                .asc();
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery
                .listPage((currPage - 1) * pageSize, pageSize);
        List<FlowProcDefVo> dataList = new ArrayList<>();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            FlowProcDefVo flowProcDefVo = new FlowProcDefVo();
            BeanUtils.copyProperties(processDefinition, flowProcDefVo);
            TbBusinessDeploy deployBusiness = deployBusinessService.getOne(new LambdaQueryWrapper<TbBusinessDeploy>()
                    .eq(TbBusinessDeploy::getDeployId, processDefinition.getDeploymentId()));
            if (ObjectUtil.isNotNull(deployBusiness)) {
                flowProcDefVo.setBusinessKey(deployBusiness.getBusinessKey());
                flowProcDefVo.setBusinessName(deployBusiness.getBusinessName());
            }
            dataList.add(flowProcDefVo);
        }
        page.setRecords(dataList);
        page.setTotal(dataList.size());
        page.setCurrent(currPage);
        page.setSize(pageSize);
        return page;
    }

    /**
     * 导入流程文件
     *
     * @param name     流程名称
     * @param category 流程分组
     * @param in       数据流
     */
    @Override
    public void importFile(String name, String category, InputStream in) {
        Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in)
                .name(name)
                .category(category)
                .tenantId(TenantThreadLocal.get())
                .deploy();
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .processDefinitionTenantId(TenantThreadLocal.get())
                .singleResult();
        repositoryService.setProcessDefinitionCategory(definition.getId(), category);
    }

    /**
     * 读取xml
     *
     * @param deployId 定义信息id
     * @return xml
     */
    @Override
    public String readXml(String deployId) throws IOException {
        ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployId)
                .singleResult();
        InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
    }

    /**
     * 读取xml
     *
     * @param deployId 流程定义id
     * @return 数据流
     */
    @Override
    public InputStream readImage(String deployId) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        //获得图片流
        DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        //输出为图片
        return diagramGenerator.generateDiagram(
                bpmnModel,
                "png",
                Collections.emptyList(),
                Collections.emptyList(),
                "宋体",
                "宋体",
                "宋体",
                null,
                1.0,
                false);

    }

    /**
     * 激活或挂起流程定义
     *
     * @param deployId 流程部署ID
     */
    @Override
    public void updateState(String deployId) {
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployId)
                .singleResult();
        if (procDef == null) {
            return;
        }
        if (procDef.isSuspended()) {
            repositoryService.activateProcessDefinitionById(procDef.getId(), true, null);
        } else {
            repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null);
        }
    }


    /**
     * 删除流程定义
     *
     * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值
     */
    @Override
    public void delete(String deployId) {
        //删除关联信息
        LambdaQueryWrapper<TbBusinessTask> taskWrapper = new LambdaQueryWrapper<>();
        taskWrapper.eq(TbBusinessTask::getDeployId, deployId);
        List<TbBusinessTask> list = taskBusinessService.list(taskWrapper);
        if (list != null) {
            List<String> taskForms = CollUtil.getFieldValues(list, "id", String.class);
            flowInfoService.delTaskFormFlow(taskForms);

        }
        repositoryService.deleteDeployment(deployId, true);
    }

    @Override
    public void bindDeployBusiness(TbBusinessDeploy deployBusiness) {
        deployBusinessService.insertOrUpdateDeployForm(deployBusiness);
    }

    @Override
    public TbBusinessDeploy getDeployBusiness(String businessKey) {
        LambdaQueryWrapper<TbBusinessDeploy> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TbBusinessDeploy::getBusinessKey, businessKey);
        return deployBusinessService.getOne(wrapper);
    }

}
